/* eslint-disable node/handle-callback-err */
const path = require('path')
const type = require('@babel/types')
const generator = require('@babel/generator').default
const traverse = require('@babel/traverse').default

class NormalModule {
  constructor (data) {
    this.name = data.name
    this.context = data.context
    this.moduleId = data.moduleId
    this.rawRequest = data.rawRequest
    this.resource = data.resource
    this.parser = data.parser
    this._source = null // 存放某个模块的源代码
    this._ast = null // 存放某个模块源代码对于的ast
    this.dependencies = [] // 定义一个空数据用于保存被依赖的模块信息
  }

  build (compilation, callback) {
    /**
    * 01 从文件中读取到将来要被加载的 module 内容
    * 02 如果当前不是 js 模块则需要 Loader 进行处理， 最终返回 js 模块
    * 03 上述的操作完成之后可以将 js 代码转化为 ast 语法树
    * 04 当前 js 模块内部可能又引用了很多其他的模块， 因此我们需要递归完成
    * 05 前面的完成之后， 我们只需要重复执行即可
    */
    this.doBuild(compilation, err => {
      this._ast = this.parser.parser(this._source)

      // 这里的 _ast 就是当前 module 的语法树， 我们可以对它进行修改，最后再将 ast 转换回 code 代码
      traverse(this._ast, {
        CallExpression: (nodePath) => {
          const node = nodePath.node

          // 定位 require 所在的节点
          if (node.callee.name === 'require') {
            // 获取原始请求路径
            const modulePath = node.arguments[0].value // './title'
            // 取出当前被加载的模块名称
            let moduleName = modulePath.split(path.posix.sep).pop() // titie
            // 当前打包只处理js
            const extName = moduleName.indexOf('.') === -1 ? '.js' : ''
            moduleName += extName // -> title.js
            // 【最终我们想要读取当前js里的内容】所以我们需要这个绝对路径
            const depResource = path.posix.join(path.posix.dirname(this.resource), moduleName)
            // 将当前模块的 id 定义为 OK
            const depModuleId = './' + path.posix.resolve(this.context, depResource) // ./src/title.js

            // console.log('doBuild',{depResource})
            // 记录当前被依赖模块的信息， 方便后面递归加载
            this.dependencies.push({
              name: this.name,
              context: this.context,
              rawRequest: moduleName,
              moduleId: depModuleId,
              resource: depResource
            })

            // 替换内容
            node.callee.name = '__webpack_require__'
            node.arguments = [type.stringLiteral(depModuleId)]
          }
        }
      })

      // 上述操作是利用 ast 按要求做了代码的修改， 下面的内容就是利用 ...... 将修改后的 ast 转换回 code
      const { code } = generator(this._ast)
      this._source = code
      callback(err)
    })
  }

  doBuild (compilation, callback) {
    this.getSource(compilation, (err, source) => {
      // console.log('getSource source callback ', source)
      this._source = source
      callback()
    })
  }

  getSource (compilation, callback) {
    compilation.inputFileSystem.readFile(this.resource, 'utf8', callback)
  }
}

module.exports = NormalModule
